package pl.edu.agh.som;

import pl.edu.agh.neural.core.*;
import pl.edu.agh.som.conscience.DefaultConscience;
import pl.edu.agh.som.conscience.IConscienceFunction;
import pl.edu.agh.som.distance.EuclideanDistance;
import pl.edu.agh.som.learning.LinearLearning;
import pl.edu.agh.som.neighborhood.INeighborhoodFunction;
import pl.edu.agh.som.neighborhood.SquareNeighborhood;
import pl.edu.agh.som.winfrequency.DefaultWinFrequencyUpdateFunction;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;

public class Runner
{

    public static void main(String[] args)
    {
        try
        {
            // train-----------------------------------------------------------
            List<String> lines = Files.readAllLines(Paths.get("train.txt"), Charset.forName("UTF-8"));
            int n = Integer.valueOf(lines.get(0));
            int dim = Integer.valueOf(lines.get(1));
            int learningSteps = Integer.valueOf(lines.get(2));

            double[][] dataVectors = readVectors(lines, 3, n, dim);


            int rows = 2;
            int columns = 2;

            INeighborhoodFunction neighborhoodFunction = new SquareNeighborhood(Math.max(rows, columns), learningSteps);
//            INeighborhoodFunction neighborhoodFunction = new NoNeighborhood();

            IConscienceFunction conscienceFunction = new DefaultConscience(1.0, rows * columns);
//            IConscienceFunction conscienceFunction =  new NoConscience();

            List<InputNeuron> inputNeurons = new ArrayList<>(dim);
            for (int i = 0; i < dim; i++)
            {
                inputNeurons.add(new InputNeuron());
            }

            InputLayer inputLayer = new InputLayer(inputNeurons, false);

            KohonenMap kohonenLayer = new KohonenMap(
                    dim,
                    rows,
                    columns,
                    new EuclideanDistance(),
                    new LinearLearning(0.06, learningSteps),
                    neighborhoodFunction,
                    conscienceFunction,
                    new DefaultWinFrequencyUpdateFunction(0.001),
                    inputLayer);

            ITrainableNetwork network =
                    new BasicTrainableNetwork(inputLayer, newArrayList(kohonenLayer));

            network.train(dataVectors, learningSteps);

            // test-----------------------------------------------------------------------
            lines = Files.readAllLines(Paths.get("test.txt"), Charset.forName("UTF-8"));
            n = Integer.valueOf(lines.get(0));

            dataVectors = readVectors(lines, 1, n, dim);

            for (int i = 0; i < n; i++)
            {
                inputLayer.setValues(dataVectors[i]);
                INeuron winner = kohonenLayer.evaluateAndGetWinner();
                for (double weight : winner.getInputConnectionsWeights())
                {
                    System.out.printf("%1$.2f ", weight);
                }
                System.out.println();
            }
        }
        catch (Exception exception)
        {
            System.out.println(exception);
        }
    }

    private static double[][] readVectors(List<String> lines, int startIndex, int n, int dim)
    {
        double[][] dataVectors = new double[n][];

        for (int i = 0; i < n; i++)
        {
            String[] vals = lines.get(i + startIndex).split("\\s+");
            dataVectors[i] = new double[dim];
            for (int j = 0; j < dim; j++)
            {
                dataVectors[i][j] = Double.parseDouble(vals[j]);
            }
        }

        return dataVectors;
    }
}
